home *** CD-ROM | disk | FTP | other *** search
/ Programmer Power Tools / Programmer Power Tools.iso / progjrn / pj_vga.arc / EVGALINE.ASM < prev    next >
Assembly Source File  |  1989-02-05  |  13KB  |  392 lines

  1. ;
  2. ; *** Listing 3 ***
  3. ;
  4. ; Fast assembler implementation of Bresenham's line drawing algorithm
  5. ; for the EGA and VGA. Works in modes 0Dh, 0Eh, 0Fh, 10h, and 12h.
  6. ; Turbo C version 2.0 near-callable.
  7. ; Bit mask accumulation technique when |DeltaX| >= |DeltaY|
  8. ;  suggested by Jim Mackraz.
  9. ;
  10. ; Assembled with TASM 1.0.
  11. ;
  12. ; By Michael Abrash.  2/4/89.
  13. ;
  14. ;****************************************************************
  15. ; C-compatible line-drawing entry point at _EVGALine.           *
  16. ; Called from Turbo C with:                                     *
  17. ;       EVGALine(X0, Y0, X1, Y1, Color);                        *
  18. ;****************************************************************
  19. ;
  20.  
  21. _TEXT   segment byte public 'CODE'
  22. _TEXT   ends
  23. CONST   segment word public 'CONST'
  24. CONST   ends
  25. _BSS    segment word public 'BSS'
  26. _BSS    ends
  27. _DATA   segment word public 'DATA'
  28. _DATA   ends
  29. DGROUP  group   CONST, _BSS, _DATA
  30.         assume  cs: _TEXT, ds: DGROUP, ss: DGROUP, es: DGROUP
  31. _TEXT   segment
  32.  
  33. ;
  34. ; Equates.
  35. ;
  36. EVGA_SCREEN_WIDTH_IN_BYTES equ  80      ;memory offset from start of
  37.                                         ; one row to start of next
  38.                                         ; in display memory
  39. EVGA_SCREEN_SEGMENT     equ     0a000h  ;display memory segment
  40. GC_INDEX                equ     3ceh    ;Graphics Controller
  41.                                         ; Index register port
  42. SET_RESET_INDEX         equ     0       ;indexes of needed
  43. ENABLE_SET_RESET_INDEX  equ     1       ; Graphics Controller
  44. BIT_MASK_INDEX          equ     8       ; registers
  45.  
  46. ;
  47. ; Stack frame.
  48. ;
  49. EVGALineParms   struc
  50.         dw      ?               ;pushed BP
  51.         dw      ?               ;pushed return address (make double
  52.                                 ; word for far call)
  53. X0      dw      ?               ;starting X coordinate of line
  54. Y0      dw      ?               ;starting Y coordinate of line
  55. X1      dw      ?               ;ending X coordinate of line
  56. Y1      dw      ?               ;ending Y coordinate of line
  57. Color   db      ?               ;color of line
  58.         db      ?               ;dummy to pad to word size
  59. EVGALineParms   ends
  60.  
  61. ;****************************************************************
  62. ; Line drawing macros.                                          *
  63. ;****************************************************************
  64.  
  65. ;
  66. ; Macro to loop through length of line, drawing each pixel in turn.
  67. ; Used for case of |DeltaX| >= |DeltaY|.
  68. ; |DeltaX|+1 points are drawn.
  69. ;
  70. ; Input:                                
  71. ;       MOVE_LEFT: 1 if DeltaX < 0, 0 else
  72. ;       AL: pixel mask for initial pixel
  73. ;       BX: |DeltaX| (X distance between start and end points)
  74. ;       DX: address of GC data register, with index register set to
  75. ;               index of Bit Mask register
  76. ;       SI: DeltaY (Y distance between start and end points)
  77. ;       ES:DI: display memory address of byte containing initial
  78. ;               pixel
  79. ;
  80. ; Output: none
  81. ;
  82. LINE1   macro   MOVE_LEFT
  83.         local   LineLoop, MoveXCoord, NextPixel, Line1End
  84.         local   MoveToNextByte, ResetBitMaskAccumulator
  85.         mov     cx,bx   ;# of pixels in line
  86.         jcxz    Line1End ;done if there are no more pixels
  87.                         ; (there's always at least the one pixel
  88.                         ; at the start location)
  89.         shl     si,1    ;DeltaY * 2
  90.         mov     bp,si   ;error term
  91.         sub     bp,bx   ;error term starts at DeltaY * 2 - DeltaX
  92.         shl     bx,1    ;DeltaX * 2
  93.         sub     si,bx   ;DeltaY * 2 - DeltaX * 2 (used in loop)
  94.         add     bx,si   ;DeltaY * 2 (used in loop)
  95.         mov     ah,al   ;set aside pixel mask for initial pixel
  96.                         ; with AL (the pixel mask accumulator) set
  97.                         ; for the initial pixel
  98. LineLoop:
  99. ;
  100. ; See if it's time to advance the Y coordinate yet.
  101. ;
  102.         and     bp,bp           ;see if error term is negative
  103.         js      MoveXCoord      ;yes, stay at the same Y coordinate
  104. ;
  105. ; Advance the Y coordinate, first writing all pixels in the current
  106. ; byte, then move the pixel mask either left or right, depending
  107. ; on MOVE_LEFT.
  108. ;
  109.         out     dx,al   ;set up bit mask for pixels in this byte
  110.         xchg    byte ptr [di],al
  111.                         ;load latches and write pixels, with bit mask
  112.                         ; preserving other latched bits. Because
  113.                         ; set/reset is enabled for all planes, the
  114.                         ; value written actually doesn't matter
  115.         add     di,EVGA_SCREEN_WIDTH_IN_BYTES   ;increment Y coordinate
  116.         add     bp,si   ;adjust error term back down
  117. ;
  118. ; Move pixel mask one pixel (either right or left, depending
  119. ; on MOVE_LEFT), adjusting display memory address when pixel mask wraps.
  120. ;
  121. if MOVE_LEFT
  122.         rol     ah,1    ;move pixel mask 1 pixel to the left
  123. else
  124.         ror     ah,1    ;move pixel mask 1 pixel to the right
  125. endif
  126.         jnc     ResetBitMaskAccumulator ;didn't wrap to next byte
  127.         jmp     short MoveToNextByte    ;did wrap to next byte
  128. ;
  129. ; Move pixel mask one pixel (either right or left, depending
  130. ; on MOVE_LEFT), adjusting display memory address and writing pixels
  131. ; in this byte when pixel mask wraps.
  132. ;
  133. MoveXCoord:
  134.         add     bp,bx   ;increment error term & keep same
  135. if MOVE_LEFT
  136.         rol     ah,1    ;move pixel mask 1 pixel to the left
  137. else
  138.         ror     ah,1    ;move pixel mask 1 pixel to the right
  139. endif
  140.         jnc     NextPixel ;if still in same byte, no need to
  141.                         ; modify display memory yet
  142.         out     dx,al   ;set up bit mask for pixels in this byte.
  143.         xchg    byte ptr [di],al
  144.                         ;load latches and write pixels, with bit mask
  145.                         ; preserving other latched bits. Because
  146.                         ; set/reset is enabled for all planes, the
  147.                         ; value written actually doesn't matter
  148. MoveToNextByte:
  149. if MOVE_LEFT
  150.         dec     di      ;next pixel is in byte to left
  151. else
  152.         inc     di      ;next pixel is in byte to right
  153. endif
  154. ResetBitMaskAccumulator:
  155.         sub     al,al   ;reset pixel mask accumulator   
  156. NextPixel:
  157.         or      al,ah   ;add the next pixel to the pixel mask
  158.                         ; accumulator
  159.         loop    LineLoop
  160. ;
  161. ; Write the pixels in the final byte.
  162. ;
  163. Line1End:
  164.         out     dx,al   ;set up bit mask for pixels in this byte.
  165.         xchg    byte ptr [di],al
  166.                         ;load latches and write pixels, with bit mask
  167.                         ; preserving other latched bits. Because
  168.                         ; set/reset is enabled for all planes, the
  169.                         ; value written actually doesn't matter
  170.         endm
  171.  
  172. ;
  173. ; Macro to loop through length of line, drawing each pixel in turn.
  174. ; Used for case of DeltaX < DeltaY.
  175. ; |DeltaY|+1 points are drawn.
  176. ;
  177. ; Input:                                
  178. ;       MOVE_LEFT: 1 if DeltaX < 0, 0 else
  179. ;       AL: pixel mask for initial pixel
  180. ;       BX: |DeltaX| (X distance between start and end points)
  181. ;       DX: address of GC data register, with index register set to
  182. ;               index of Bit Mask register
  183. ;       SI: DeltaY (Y distance between start and end points)
  184. ;       ES:DI: display memory address of byte containing initial
  185. ;               pixel
  186. ;
  187. ; Output: none
  188. ;
  189. LINE2   macro   MOVE_LEFT
  190.         local   LineLoop, MoveYCoord, ETermAction, Line2End
  191.         mov     cx,si   ;# of pixels in line
  192.         shl     bx,1    ;DeltaX * 2
  193.         mov     bp,bx   ;error term
  194.         sub     bp,si   ;error term starts at DeltaX * 2 - DeltaY
  195.         shl     si,1    ;DeltaY * 2
  196.         sub     bx,si   ;DeltaX * 2 - DeltaY * 2 (used in loop)
  197.         add     si,bx   ;DeltaX * 2 (used in loop)
  198. ;
  199. ; Set up initial bit mask & draw initial pixel.
  200. ;
  201.         out     dx,al
  202.         xchg    byte ptr [di],ah
  203.                         ;load latches and write pixel, with bit mask
  204.                         ; preserving other latched bits. Because
  205.                         ; set/reset is enabled for all planes, the
  206.                         ; value written actually doesn't matter
  207.         jcxz    Line2End ;done if there are no more pixels
  208.                         ; (there's always at least the one pixel
  209.                         ; at the start location)
  210. LineLoop:
  211. ;
  212. ; See if it's time to advance the X coordinate yet.
  213. ;
  214.         and     bp,bp           ;see if error term is negative
  215.         jns     ETermAction     ;no, advance X coordinate
  216.         add     bp,si           ;increment error term & keep same
  217.         jmp     short MoveYCoord ; X coordinate
  218. ETermAction:
  219. ;
  220. ; Move pixel mask one pixel (either right or left, depending
  221. ; on MOVE_LEFT), adjusting display memory address when pixel mask wraps.
  222. ;
  223. if MOVE_LEFT
  224.         rol     al,1
  225.         sbb     di,0
  226. else
  227.         ror     al,1
  228.         adc     di,0
  229. endif
  230.         out     dx,al   ;set new bit mask
  231.         add     bp,bx   ;adjust error term back down
  232. ;
  233. ; Advance Y coordinate.
  234. ;
  235. MoveYCoord:
  236.         add     di,EVGA_SCREEN_WIDTH_IN_BYTES
  237. ;
  238. ; Write the next pixel.
  239. ;
  240.         xchg    byte ptr [di],ah
  241.                         ;load latches and write pixel, with bit mask
  242.                         ; preserving other latched bits. Because
  243.                         ; set/reset is enabled for all planes, the
  244.                         ; value written actually doesn't matter
  245. ;
  246.         loop    LineLoop
  247. Line2End:
  248.         endm
  249.  
  250. ;****************************************************************
  251. ; Line drawing routine.                                         *
  252. ;****************************************************************
  253.  
  254.         public  _EVGALine
  255. _EVGALine       proc    near
  256.         push    bp
  257.         mov     bp,sp
  258.         push    si      ;preserve register variables
  259.         push    di
  260.         push    ds
  261. ;
  262. ; Point DS to display memory.
  263. ;
  264.         mov     ax,EVGA_SCREEN_SEGMENT
  265.         mov     ds,ax
  266. ;
  267. ; Set the Set/Reset and Set/Reset Enable registers for
  268. ; the selected color.
  269. ;
  270.         mov     dx,GC_INDEX
  271.         mov     al,SET_RESET_INDEX
  272.         out     dx,al
  273.         inc     dx
  274.         mov     al,[bp+Color]
  275.         out     dx,al
  276.         dec     dx
  277.         mov     al,ENABLE_SET_RESET_INDEX
  278.         out     dx,al
  279.         inc     dx
  280.         mov     al,0ffh
  281.         out     dx,al
  282. ;
  283. ; Get DeltaY.
  284. ;
  285.         mov     si,[bp+Y1]      ;line Y start
  286.         mov     ax,[bp+Y0]      ;line Y end, used later in
  287.                                 ;calculating the start address
  288.         sub     si,ax           ;calculate DeltaY
  289.         jns     CalcStartAddress ;if positive, we're set
  290. ;
  291. ; DeltaY is negative -- swap coordinates so we're always working
  292. ; with a positive DeltaY.
  293. ;
  294.         mov     ax,[bp+Y1]      ;set line start to Y1, for use
  295.                                 ; in calculating the start address
  296.         mov     dx,[bp+X0]
  297.         xchg    dx,[bp+X1]
  298.         mov     [bp+X0],dx      ;swap X coordinates
  299.         neg     si              ;convert to positive DeltaY
  300. ;
  301. ; Calculate the starting address in display memory of the line.
  302. ; Hardwired for a screen width of 80 bytes.
  303. ;
  304. CalcStartAddress:
  305.         shl     ax,1    ;Y0 * 2 ;Y0 is already in AX
  306.         shl     ax,1    ;Y0 * 4
  307.         shl     ax,1    ;Y0 * 8
  308.         shl     ax,1    ;Y0 * 16
  309.         mov     di,ax
  310.         shl     ax,1    ;Y0 * 32
  311.         shl     ax,1    ;Y0 * 64
  312.         add     di,ax   ;Y0 * 80
  313.         mov     dx,[bp+X0]
  314.         mov     cl,dl   ;set aside lower 3 bits of column for
  315.         and     cl,7    ; pixel masking
  316.         shr     dx,1
  317.         shr     dx,1
  318.         shr     dx,1    ;get byte address of column (X0/8)
  319.         add     di,dx   ;offset of line start in display segment
  320. ;
  321. ; Set up GC Index register to point to the Bit Mask register.
  322. ;
  323.         mov     dx,GC_INDEX
  324.         mov     al,BIT_MASK_INDEX
  325.         out     dx,al
  326.         inc     dx      ;leave DX pointing to the GC Data register
  327. ;
  328. ; Set up pixel mask (in-byte pixel address).
  329. ;
  330.         mov     al,80h
  331.         shr     al,cl
  332. ;
  333. ; Calculate DeltaX.
  334. ;
  335.         mov     bx,[bp+X1]
  336.         sub     bx,[bp+X0]
  337. ;
  338. ; Handle correct one of four octants.
  339. ;
  340.         js      NegDeltaX
  341.         cmp     bx,si
  342.         jb      Octant1
  343. ;
  344. ; DeltaX >= DeltaY >= 0.
  345. ;
  346.         LINE1   0
  347.         jmp     EVGALineDone
  348. ;
  349. ; DeltaY > DeltaX >= 0.
  350. ;
  351. Octant1:
  352.         LINE2   0
  353.         jmp     short EVGALineDone
  354. ;
  355. NegDeltaX:
  356.         neg     bx      ;|DeltaX|
  357.         cmp     bx,si
  358.         jb      Octant2
  359. ;
  360. ; |DeltaX| >= DeltaY and DeltaX < 0.
  361. ;
  362.         LINE1   1
  363.         jmp     short EVGALineDone
  364. ;
  365. ; |DeltaX| < DeltaY and DeltaX < 0.
  366. ;
  367. Octant2:
  368.         LINE2   1
  369. ;
  370. EVGALineDone:
  371. ;
  372. ; Restore EVGA state.
  373. ;
  374.         mov     al,0ffh
  375.         out     dx,al   ;set Bit Mask register to 0ffh
  376.         dec     dx
  377.         mov     al,ENABLE_SET_RESET_INDEX
  378.         out     dx,al
  379.         inc     dx
  380.         sub     al,al
  381.         out     dx,al   ;set Enable Set/Reset register to 0
  382. ;
  383.         pop     ds
  384.         pop     di
  385.         pop     si
  386.         pop     bp
  387.         ret
  388. _EVGALine       endp
  389.  
  390. _TEXT   ends
  391.         end
  392.